// stores information used in parsing ABaCus programs

using namespace std;

char getDelimitedChar(char);
void printVector(vector<char>);
int powerOfTen(int);

#include "errors.cpp"

class subroutine: public vector<char>
{
   public:
      string name;
};

class program: public vector<char>
{
   public:
      program(): purged(0), valueFlag(0), nameFlag(0), printFlag(0), tempchar(0), tempint(0), datatype(0), indicator(0) {}
   
      void parse();
      void print();
      void purge();
      void debug();
      
      unsigned parse_NewCharValue(unsigned int);
      unsigned parse_NewIntValue(unsigned int);
      
      unsigned parse_ConditionIf(unsigned int);
      
      bool nameExists(vector<char>);
      unsigned int nameSearch(vector<char>);
      short int nameType(vector<char>);
      
      bool valueFlag, nameFlag, printFlag;
      char tempchar;
      vector<int> digits;
      int tempint, datatype;
      unsigned int indicator;
      vector<char> tempname;
      vector<char> tempname0; // Another temp name, for conditionals et al
   private:
      vector<int> ints;
      vector< vector<char> > inames;
      vector<char> chars;
      vector< vector<char> > cnames;
      vector< subroutine > subroutines;
      bool purged;
};

#include "parser_definevariables.cpp"
#include "parser_conditions.cpp"

void program::parse()
{
   if ( !purged ) { purge(); }
   
   for ( unsigned int i = 0; i < size(); i ++ )
   {
      if ( at(i) == 'c' )
      {
         i ++; // move to opening value delimiter
         i = parse_NewCharValue(i);
      }
      
      else if ( at(i) == 'i' )
      {
         i ++; // move to first argument
         i = parse_NewIntValue(i);
      }
      
      else if ( at(i) == '<' )
      {
         i ++;
         
         if ( at(i) == '\'' )
         {
            i ++;
            
            while ( at(i) != '\'' )
            {
               cout << at(i);
               i ++;
            }
         }
         
         else if ( at(i) == '\"' )
         {
            i ++;
            while ( at(i) != '"' )
            {
               tempname.push_back(at(i));
               i ++;
            }
            if ( nameExists(tempname) )
            {
               indicator = nameSearch(tempname);
               datatype = nameType(tempname);
               
               switch ( datatype )
               {
                  case 0:
                     cout << ints[indicator] << endl;
                     break;
                  case 1:
                     cout << chars[indicator] << endl;
                     break;
               }
            }
            else { ErrorInvalidName(i, tempname); }
         }
         else { ErrorWrongArgument(i, '<'); }
      }
      else if ( at(i) == '>' )
      {
         i ++; // move to argument
         if ( at(i) == '\"' )
         {
            i ++; // move into delimiter
            while ( at(i) != '\"' )
            {
               tempname.push_back(at(i));
            }
            
            if ( nameExists(tempname) ) { datatype = nameType(tempname); }
            else {  ErrorInvalidName(i,tempname); }
         }
         else if ( at(i) == '*' )
         {
            i ++;
            if ( at(i) == 'c' )
            {
               datatype = 1;
               i += 2; // move onto name
            }
            else if ( at(i) == 'i' )
            {
               datatype = 0;
               i += 2; // move onto name
            }
            
            if ( at(i) == '\"' )
            {
               i ++;
               while ( at(i) != '\"' )
               {
                  tempname.push_back(at(i));
                  i ++;
               }
               if ( nameExists(tempname) )
               {
                  i ++; // move out of first name
                  datatype = nameType(tempname);
                  cout << at(i);
               }
            }
            else { ErrorWrongArgument(i,'>'); }
            
            if ( nameExists(tempname) ) { ErrorPredefinedName(i,tempname); }
            else
            {
               switch ( datatype )
               {
                  case 0:
                     inames.push_back(tempname);
                     break;
                  case 1:
                     cnames.push_back(tempname);
                     break;
               }
            }
         }
         
         else { ErrorWrongArgument(i,'>'); }
         
         switch ( datatype )
         {
            case 0:
               cin >> ints[nameSearch(tempname)];
               break;
            case 1:
               cin >> chars[nameSearch(tempname)];
               break;
         }
      }
      
      else if ( at(i) == 'I' ) { i = parse_ConditionIf(i); }
      
      else if ( at(i) == 'G' )
      {
         i ++;
         if ( at(i) == '\'' )
         {
            i ++;
            if ( '0' <= at(i) <= '9' )
            {
               while ( at(i) != '\'' )
               {
                  digits.push_back(at(i)-48);
                  i ++;
               }
               cout << endl;
               for ( unsigned int j = 0; j < digits.size(); j ++ )
               {
                  tempint += digits[j]*powerOfTen(digits.size()-j-1);
               }
               if ( tempint < size() )
               {
                  i = tempint - 1;
               }
               else { ErrorSegFault(i); }
            }
            else { ErrorIntegerPosition(i); }
         }
         else if ( at(i) == '\"' )
         {
            i ++;
            while ( at(i) != '\"' )
            {
               tempname.push_back(at(i));
               i ++;
            }
            if ( nameExists(tempname) )
            {
               if ( !nameType(tempname) )
               {
                  if ( nameSearch(tempname) < size() )
                  {
                     i = nameSearch(tempname) - 1;
                  }
                  else { ErrorSegFault(i); }
               }
               else { ErrorIntegerPosition(i); }
            }
            else { ErrorInvalidName('i',tempname); }
         }
         else { ErrorWrongArgument(i,'G'); }
      }
      
      // NEW COMMANDS GO JUST ABOVE THIS LINE
      
      valueFlag = 0;
      nameFlag = 0;
      printFlag = 0;
      tempint = 0;
      tempname.clear();
      tempname0.clear();
      digits.clear();
   }
}

void program::print()
{
   for ( unsigned int i = 0; i < size(); i ++ )
   {
      cout << at(i);
   }
}

void program::purge()
{
   bool commentflag = 0; // flags comments
   
   vector<unsigned int> flagged;
   
   for ( unsigned int i = 0; i < size(); i ++ )
   {
      if ( at(i) == '/' ) { commentflag = !commentflag; }
      
      while ( commentflag )
      {
         erase( begin() + i );
         if ( at(i) == '/' )
         {
            commentflag = !commentflag;
            erase( begin() + i );
         }
      }
      
      if ( at(i) == '\n' || at(i) == ' ' ) { flagged.push_back(i); }
   }
   
   flagged.erase(flagged.end()-1); // erase last element
   
   while ( flagged.size() > 0 )
   {
      erase(begin() + flagged[0]);
      flagged.erase(flagged.begin());
      for ( unsigned int i = 0; i < flagged.size(); i ++ )
      {
         flagged[i] --;
      }
   }
   
   purged = 1;
}

void program::debug()
{
   // use this to inject code
   // does nothing at present
}

bool program::nameExists(vector<char> input)
{
   for ( unsigned int i = 0; i < inames.size(); i ++ )
   {
      if ( inames[i] == input ) { return 1; }
   }
   
   for ( unsigned int i = 0; i < cnames.size(); i ++ )
   {
      if ( cnames[i] == input ) { return 1; }
   }
   
   return 0;
}

unsigned int program::nameSearch(vector<char> input)
{
   // only call this function after an "if ( nameExists() )" statement
   // otherwise it will fail
   
   for ( unsigned int i = 0; i < inames.size(); i ++ )
   {
      if ( inames[i] == input ) { return i; }
   }
   
   for ( unsigned int i = 0; i < cnames.size(); i ++ )
   {
      if ( cnames[i] == input ) { return i; }
   }
   
   cout << "ERROR: nameSearch function used without name check!";
   assert(1==0);
}

short program::nameType(vector<char> input)
{
   // same as nameSearch()
   for ( unsigned int i = 0; i < inames.size(); i ++ )
   {
      if ( inames[i] == input ) { return 0; }
   }
   
   for ( unsigned int i = 0; i < cnames.size(); i ++ )
   {
      if ( cnames[i] == input ) { return 1; }
   }
   
   cout << "ERROR: nameType function used without name check!";
   assert(1==0);
}

char getDelimitedChar(char input)
{
   switch ( input )
   {
      case 'a':
         return '\a';
         break;
      case 'b':
         return '\b';
         break;
      case 'f':
         return '\f';
         break;
      case 'n':
         return '\n';
         break;
      case 'r':
         return '\r';
         break;
      case 't':
         return '\t';
         break;
      case 'v':
         return '\v';
         break;
      case '\'':
         return '\'';
         break;
      case '\\':
         return '\\';
         break;
   }
}

void printVector(vector<char> input)
{
   for ( unsigned int i = 0; i < input.size(); i ++ )
   {
      cout << input[i];
   }
}

int powerOfTen(int input)
{
   int output = 1;
   
   for ( unsigned int i = 0; i < input; i ++ )
   {
      output *= 10;
   }
   
   return output;
}
